package io.gitee.lglbc.easy.security.core.util;


import com.alibaba.fastjson2.JSON;
import io.gitee.lglbc.easy.security.core.exception.TokenException;
import io.gitee.lglbc.easy.security.core.token.EasyPayload;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.util.DigestUtils;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;

/**
 * @author 乐哥聊编程
 */
public class JwtTokenUtil {
    private static final String slat = "dafa#$%^&*()VBNM<>)(*&^%$%^&*()";
    public static String generateTokenByHMAC(String payloadStr, String secret) {
        JWSObject jwsObject = null;
        try {
            JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256)
                    .type(JOSEObjectType.JWT).build();
            Payload payload = new Payload(payloadStr);
            jwsObject = new JWSObject(jwsHeader, payload);
            JWSSigner jwsSigner = new MACSigner(md5(secret));
            jwsObject.sign(jwsSigner);
        } catch (JOSEException e) {
            throw new TokenException("400", "token生成失败!");
        }
        return jwsObject.serialize();
    }

    public static EasyPayload checkTokenByHMAC(String token, String secret) {
        try {
            JWSObject jwsObject = JWSObject.parse(token);
            //创建HMAC验证器
            JWSVerifier jwsVerifier = new MACVerifier(md5(secret));
            if (!jwsObject.verify(jwsVerifier)) {
                throw new TokenException("401", "token签名不合法!");
            }
            String payload = jwsObject.getPayload().toString();
            EasyPayload payloadDto = JSON.parseObject(payload, EasyPayload.class);
            if (payloadDto.getExp() < new Date().getTime()) {
                throw new TokenException("402", "token已过期!");
            }
            return payloadDto;
        } catch (Exception e) {
            throw new TokenException("403", "token校验失败!");
        }
    }

    public static String generateTokenByRSA(String payloadStr, RSAKey rsaKey) {
        JWSObject jwsObject = null;
        try {
            JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)
                    .type(JOSEObjectType.JWT)
                    .build();
            Payload payload = new Payload(payloadStr);
            jwsObject = new JWSObject(jwsHeader, payload);
            JWSSigner jwsSigner = new RSASSASigner(rsaKey, true);
            jwsObject.sign(jwsSigner);
        } catch (JOSEException e) {
            throw new TokenException("400", "token verify failed");
        }
        return jwsObject.serialize();
    }

    public static EasyPayload checkTokenByRSA(String token, RSAKey rsaKey) {
        try {
            JWSObject jwsObject = JWSObject.parse(token);
            RSAKey publicRsaKey = rsaKey.toPublicJWK();
            JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey);
            if (!jwsObject.verify(jwsVerifier)) {
                throw new TokenException("401", "token key is invalid");
            }
            String payload = jwsObject.getPayload().toString();
            EasyPayload payloadDto = JSON.parseObject(payload, EasyPayload.class);
            if (payloadDto.getExp() < new Date().getTime()) {
                throw new TokenException("402", "token expire");
            }
            return payloadDto;
        } catch (Exception e) {
            throw new TokenException("403", "token verify failed");
        }
    }

    public static RSAKey getRSAKey(String jksName, String jksSecret) {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jksName), jksSecret.toCharArray());
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair("jwt", jksSecret.toCharArray());
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        return new RSAKey.Builder(publicKey).privateKey(privateKey).build();
    }

    public static String md5(String str) {
        String base = str +"/"+slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }
}
